/*
** This file contains code that is specific to windows.
*/
#include "System.h"
#if SYSTEM_OS_WIN               /* This file is used for windows only */

/*
** A Note About Memory Allocation:
**
** This driver uses malloc()/free() directly rather than going through the APPID-wrappers system_malloc()/system_free().  Those wrappers
** are designed for use on embedded systems where memory is scarce and malloc failures happen frequently.  Win32 does not typically run on
** embedded systems, and when it does the developers normally have bigger problems to worry about than running out of memory.  So there is not
** a compelling need to use the wrappers.
**
** But there is a good reason to not use the wrappers.  If we use the wrappers then we will get simulated malloc() failures within this
** driver.  And that causes all kinds of problems for our tests.  We could enhance SQLite to deal with simulated malloc failures within
** the OS driver, but the code to deal with those failure would not be exercised on Linux (which does not need to malloc() in the driver)
** and so we would have difficulty writing coverage tests for that code.  Better to leave the code out, we think.
**
** The point of this discussion is as follows:  When creating a new OS layer for an embedded system, if you use this file as an example,
** avoid the use of malloc()/free().  Those routines work ok on windows desktops but not so well in embedded systems.
*/
#include <winbase.h>

#ifdef __CYGWIN__
# include <sys/cygwin.h>
#endif

/*
** Macros used to determine whether or not to use threads.
*/
#if defined(THREADSAFE) && THREADSAFE
# define SYSTEM_W32_THREADS 1
#endif

/*
** Include code that is common to all os_*.c files
*/
#include "System.OS+Common.h"

/*
** Some microsoft compilers lack this definition.
*/
#ifndef INVALID_FILE_ATTRIBUTES
# define INVALID_FILE_ATTRIBUTES ((DWORD)-1) 
#endif

/*
** Determine if we are dealing with WindowsCE - which has a much reduced API.
*/
#if SYSTEM_OS_WINCE
# define AreFileApisANSI() 1
# define FormatMessageW(a,b,c,d,e,f,g) 0
#endif

/* Forward references */
typedef struct winShm winShm;           /* A connection to shared-memory */
typedef struct winShmNode winShmNode;   /* A region of shared-memory */

/*
** WinCE lacks native support for file locking so we have to fake it with some code of our own.
*/
#if SYSTEM_OS_WINCE
typedef struct winceLock
{
	int nReaders;       /* Number of reader locks obtained */
	BOOL bPending;      /* Indicates a pending lock has been obtained */
	BOOL bReserved;     /* Indicates a reserved lock has been obtained */
	BOOL bExclusive;    /* Indicates an exclusive lock has been obtained */
} winceLock;
#endif

/*
** The winFile structure is a subclass of sqlite3_file* specific to the win32 portability layer.
*/
typedef struct winFile winFile;
struct winFile
{
	const system_io_methods *pMethod; /*** Must be first ***/
	system_vfs *pVfs;      /* The VFS used to open this file */
	HANDLE h;               /* Handle for accessing the file */
	unsigned char locktype; /* Type of lock currently held on this file */
	short sharedLockByte;   /* Randomly chosen byte used as a shared lock */
	DWORD lastErrno;        /* The Windows errno from the last I/O error */
	DWORD sectorSize;       /* Sector size of the device file is on */
	winShm *pShm;           /* Instance of shared memory on this file */
	const char *zPath;      /* Full pathname of this file */
	int szChunk;            /* Chunk size configured by FCNTL_CHUNK_SIZE */
#if SYSTEM_OS_WINCE
	WCHAR *zDeleteOnClose;  /* Name of file to delete when closing */
	HANDLE hMutex;          /* Mutex used to control access to shared lock */  
	HANDLE hShared;         /* Shared memory segment used for locking */
	winceLock local;        /* Locks obtained by this instance of winFile */
	winceLock *shared;      /* Global shared lock memory for the file  */
#endif
};

/*
** Forward prototypes.
*/
static int getSectorSize(system_vfs *pVfs, const char *zRelative); /* UTF-8 file name */

/*
** The following variable is (normally) set once and never changes thereafter.  It records whether the operating system is Win95
** or WinNT.
**
** 0:   Operating system unknown.
** 1:   Operating system is Win95.
** 2:   Operating system is WinNT.
**
** In order to facilitate testing on a WinNT system, the test fixture can manually set this value to 1 to emulate Win98 behavior.
*/
#ifdef SYSTEM_TEST
int system_os_type = 0;
#else
static int system_os_type = 0;
#endif

/*
** Return true (non-zero) if we are running under WinNT, Win2K, WinXP, or WinCE.  Return false (zero) for Win95, Win98, or WinME.
**
** Here is an interesting observation:  Win95, Win98, and WinME lack the LockFileEx() API.  But we can still statically link against that
** API as long as we don't call it when running Win95/98/ME.  A call to this routine is used to determine if the host is Win95/98/ME or
** WinNT/2K/XP so that we will know whether or not we can safely call the LockFileEx() API.
*/
#if SYSTEM_OS_WINCE
# define isNT()  (1)
#else
static int isNT(void)
{
    if (system_os_type==0)
	{
		OSVERSIONINFO sInfo;
		sInfo.dwOSVersionInfoSize = sizeof(sInfo);
		GetVersionEx(&sInfo);
		system_os_type = (sInfo.dwPlatformId==VER_PLATFORM_WIN32_NT ? 2 : 1);
    }
    return (system_os_type==2);
}
#endif /* SYSTEM_OS_WINCE */

/*
** Convert a UTF-8 string to microsoft unicode (UTF-16?). 
**
** Space to hold the returned string is obtained from malloc.
*/
static WCHAR *utf8ToUnicode(const char *zFilename)
{
	int nChar;
	WCHAR *zWideFilename;
	nChar = MultiByteToWideChar(CP_UTF8, 0, zFilename, -1, NULL, 0);
	zWideFilename = (WCHAR*)malloc(nChar*sizeof(zWideFilename[0]));
	if (zWideFilename==0)
		return 0;
	nChar = MultiByteToWideChar(CP_UTF8, 0, zFilename, -1, zWideFilename, nChar);
	if (nChar==0)
	{
		free(zWideFilename);
		zWideFilename = 0;
	}
	return zWideFilename;
}

/*
** Convert microsoft unicode to UTF-8.  Space to hold the returned string is obtained from malloc().
*/
static char *unicodeToUtf8(const WCHAR *zWideFilename)
{
	int nByte;
	char *zFilename;
	nByte = WideCharToMultiByte(CP_UTF8, 0, zWideFilename, -1, 0, 0, 0, 0);
	zFilename = (char*)malloc( nByte );
	if (zFilename==0)
		return 0;
	nByte = WideCharToMultiByte(CP_UTF8, 0, zWideFilename, -1, zFilename, nByte, 0, 0);
	if (nByte == 0)
	{
		free(zFilename);
		zFilename = 0;
	}
	return zFilename;
}

/*
** Convert an ansi string to microsoft unicode, based on the current codepage settings for file apis.
** 
** Space to hold the returned string is obtained from malloc.
*/
static WCHAR *mbcsToUnicode(const char *zFilename)
{
	int nByte;
	WCHAR *zMbcsFilename;
	int codepage = (AreFileApisANSI() ? CP_ACP : CP_OEMCP);
	nByte = MultiByteToWideChar(codepage, 0, zFilename, -1, NULL,0) * sizeof(WCHAR);
	zMbcsFilename = (WCHAR*)malloc(nByte*sizeof(zMbcsFilename[0]));
	if (zMbcsFilename==0)
		return 0;
	nByte = MultiByteToWideChar(codepage, 0, zFilename, -1, zMbcsFilename, nByte);
	if (nByte==0)
	{
		free(zMbcsFilename);
		zMbcsFilename = 0;
	}
	return zMbcsFilename;
}

/*
** Convert microsoft unicode to multibyte character string, based on the user's Ansi codepage.
**
** Space to hold the returned string is obtained from malloc().
*/
static char *unicodeToMbcs(const WCHAR *zWideFilename)
{
	int nByte;
	char *zFilename;
	int codepage = (AreFileApisANSI() ? CP_ACP : CP_OEMCP);
	nByte = WideCharToMultiByte(codepage, 0, zWideFilename, -1, 0, 0, 0, 0);
	zFilename = (char*)malloc(nByte);
	if (zFilename==0)
		return 0;
	nByte = WideCharToMultiByte(codepage, 0, zWideFilename, -1, zFilename, nByte, 0, 0);
	if (nByte == 0)
	{
		free(zFilename);
		zFilename = 0;
	}
	return zFilename;
}

/*
** Convert multibyte character string to UTF-8.  Space to hold the returned string is obtained from malloc().
*/
char *system_win32_mbcs_to_utf8(const char *zFilename)
{
	char *zFilenameUtf8;
	WCHAR *zTmpWide;
	zTmpWide = mbcsToUnicode(zFilename);
	if (zTmpWide==0)
		return 0;
	zFilenameUtf8 = unicodeToUtf8(zTmpWide);
	free(zTmpWide);
	return zFilenameUtf8;
}

/*
** Convert UTF-8 to multibyte character string.  Space to hold the returned string is obtained from malloc().
*/
static char *utf8ToMbcs(const char *zFilename)
{
	char *zFilenameMbcs;
	WCHAR *zTmpWide;
	zTmpWide = utf8ToUnicode(zFilename);
	if (zTmpWide==0)
		return 0;
	zFilenameMbcs = unicodeToMbcs(zTmpWide);
	free(zTmpWide);
	return zFilenameMbcs;
}

/*
** This section contains code for WinCE only.
*/
#if SYSTEM_OS_WINCE
/*
** WindowsCE does not have a localtime() function.  So create a substitute.
*/
#include <time.h>
struct tm *__cdecl localtime(const time_t *t)
{
	static struct tm y;
	FILETIME uTm, lTm;
	SYSTEMTIME pTm;
	INT64_TYPE t64;
	t64 = *t;
	t64 = (t64 + 11644473600)*10000000;
	uTm.dwLowDateTime = (DWORD)(t64 & 0xFFFFFFFF);
	uTm.dwHighDateTime= (DWORD)(t64 >> 32);
	FileTimeToLocalFileTime(&uTm,&lTm);
	FileTimeToSystemTime(&lTm,&pTm);
	y.tm_year = pTm.wYear - 1900;
	y.tm_mon = pTm.wMonth - 1;
	y.tm_wday = pTm.wDayOfWeek;
	y.tm_mday = pTm.wDay;
	y.tm_hour = pTm.wHour;
	y.tm_min = pTm.wMinute;
	y.tm_sec = pTm.wSecond;
	return &y;
}

/* This will never be called, but defined to make the code compile */
#define GetTempPathA(a,b)
#define LockFile(a,b,c,d,e)       winceLockFile(&a, b, c, d, e)
#define UnlockFile(a,b,c,d,e)     winceUnlockFile(&a, b, c, d, e)
#define LockFileEx(a,b,c,d,e,f)   winceLockFileEx(&a, b, c, d, e, f)
#define HANDLE_TO_WINFILE(a) (winFile*)&((char*)a)[-(int)offsetof(winFile,h)]

/*
** Acquire a lock on the handle h
*/
static void winceMutexAcquire(HANDLE h)
{
	DWORD dwErr;
	do
	{
		dwErr = WaitForSingleObject(h, INFINITE);
	} while (dwErr != WAIT_OBJECT_0 && dwErr != WAIT_ABANDONED);
}

/*
** Release a lock acquired by winceMutexAcquire()
*/
#define winceMutexRelease(h) ReleaseMutex(h)

/*
** Create the mutex and shared memory used for locking in the file
** descriptor pFile
*/
static BOOL winceCreateLock(const char *zFilename, winFile *pFile)
{
	WCHAR *zTok;
	WCHAR *zName = utf8ToUnicode(zFilename);
	BOOL bInit = TRUE;
	/* Initialize the local lockdata */
	ZeroMemory(&pFile->local, sizeof(pFile->local));
	/* Replace the backslashes from the filename and lowercase it to derive a mutex name. */
	zTok = CharLowerW(zName);
	for (;*zTok;zTok++)
		if (*zTok == '\\')
			*zTok = '_';
	/* Create/open the named mutex */
	pFile->hMutex = CreateMutexW(NULL, FALSE, zName);
	if (!pFile->hMutex)
	{
		pFile->lastErrno = GetLastError();
		free(zName);
		return FALSE;
	}
	/* Acquire the mutex before continuing */
	winceMutexAcquire(pFile->hMutex);
	/*
	** Since the names of named mutexes, semaphores, file mappings etc are case-sensitive, take advantage of that by uppercasing the mutex name
	** and using that as the shared filemapping name.
	*/
	CharUpperW(zName);
	pFile->hShared = CreateFileMappingW(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, sizeof(winceLock), zName);  

	/* Set a flag that indicates we're the first to create the memory so it must be zero-initialized */
	if (GetLastError() == ERROR_ALREADY_EXISTS)
	    bInit = FALSE;
	free(zName);
	/* If we succeeded in making the shared memory handle, map it. */
	if (pFile->hShared)
	{
		pFile->shared = (winceLock*)MapViewOfFile(pFile->hShared, FILE_MAP_READ|FILE_MAP_WRITE, 0, 0, sizeof(winceLock));
		/* If mapping failed, close the shared memory handle and erase it */
		if (!pFile->shared)
		{
			pFile->lastErrno = GetLastError();
			CloseHandle(pFile->hShared);
			pFile->hShared = NULL;
		}
	}
	/* If shared memory could not be created, then close the mutex and fail */
	if (pFile->hShared == NULL)
	{
		winceMutexRelease(pFile->hMutex);
		CloseHandle(pFile->hMutex);
		pFile->hMutex = NULL;
		return FALSE;
	}
	/* Initialize the shared memory if we're supposed to */
	if (bInit)
		ZeroMemory(pFile->shared, sizeof(winceLock));
	winceMutexRelease(pFile->hMutex);
	return TRUE;
}

/*
** Destroy the part of winFile that deals with wince locks
*/
static void winceDestroyLock(winFile *pFile)
{
	if (pFile->hMutex)
	{
		/* Acquire the mutex */
		winceMutexAcquire(pFile->hMutex);
		/* The following blocks should probably assert in debug mode, but they are to cleanup in case any locks remained open */
		if (pFile->local.nReaders)
			pFile->shared->nReaders --;
		if (pFile->local.bReserved)
			pFile->shared->bReserved = FALSE;
		if (pFile->local.bPending)
			pFile->shared->bPending = FALSE;
		if (pFile->local.bExclusive)
			pFile->shared->bExclusive = FALSE;
		/* De-reference and close our copy of the shared memory handle */
		UnmapViewOfFile(pFile->shared);
		CloseHandle(pFile->hShared);
		/* Done with the mutex */
		winceMutexRelease(pFile->hMutex);    
		CloseHandle(pFile->hMutex);
		pFile->hMutex = NULL;
	}
}

/* 
** An implementation of the LockFile() API of windows for wince
*/
static BOOL winceLockFile(HANDLE *phFile, DWORD dwFileOffsetLow, DWORD dwFileOffsetHigh, DWORD nNumberOfBytesToLockLow, DWORD nNumberOfBytesToLockHigh)
{
	winFile *pFile = HANDLE_TO_WINFILE(phFile);
	BOOL bReturn = FALSE;
	UNUSED_PARAMETER(dwFileOffsetHigh);
	UNUSED_PARAMETER(nNumberOfBytesToLockHigh);
	if (!pFile->hMutex)
		return TRUE;
	winceMutexAcquire(pFile->hMutex);
	/* Wanting an exclusive lock? */
	if (dwFileOffsetLow == (DWORD)SHARED_FIRST && nNumberOfBytesToLockLow == (DWORD)SHARED_SIZE)
	{
		if (pFile->shared->nReaders == 0 && pFile->shared->bExclusive == 0)
		{
			pFile->shared->bExclusive = TRUE;
			pFile->local.bExclusive = TRUE;
			bReturn = TRUE;
		}
	}
	/* Want a read-only lock? */
	else if (dwFileOffsetLow == (DWORD)SHARED_FIRST && nNumberOfBytesToLockLow == 1)
	{
		if (pFile->shared->bExclusive == 0)
		{
			pFile->local.nReaders ++;
			if (pFile->local.nReaders == 1)
				pFile->shared->nReaders ++;
			bReturn = TRUE;
		}
	}
	/* Want a pending lock? */
	else if (dwFileOffsetLow == (DWORD)PENDING_BYTE && nNumberOfBytesToLockLow == 1)
	{
		/* If no pending lock has been acquired, then acquire it */
		if (pFile->shared->bPending == 0)
		{
			pFile->shared->bPending = TRUE;
			pFile->local.bPending = TRUE;
			bReturn = TRUE;
		}
	}
	/* Want a reserved lock? */
	else if (dwFileOffsetLow == (DWORD)RESERVED_BYTE && nNumberOfBytesToLockLow == 1)
	{
		if (pFile->shared->bReserved == 0)
		{
			pFile->shared->bReserved = TRUE;
			pFile->local.bReserved = TRUE;
			bReturn = TRUE;
		}
	}
	winceMutexRelease(pFile->hMutex);
	return bReturn;
}

/*
** An implementation of the UnlockFile API of windows for wince
*/
static BOOL winceUnlockFile(HANDLE *phFile, DWORD dwFileOffsetLow, DWORD dwFileOffsetHigh, DWORD nNumberOfBytesToUnlockLow, DWORD nNumberOfBytesToUnlockHigh)
{
	winFile *pFile = HANDLE_TO_WINFILE(phFile);
	BOOL bReturn = FALSE;
	UNUSED_PARAMETER(dwFileOffsetHigh);
	UNUSED_PARAMETER(nNumberOfBytesToUnlockHigh);
	if (!pFile->hMutex)
		return TRUE;
	winceMutexAcquire(pFile->hMutex);
	/* Releasing a reader lock or an exclusive lock */
	if (dwFileOffsetLow == (DWORD)SHARED_FIRST)
	{
		/* Did we have an exclusive lock? */
		if (pFile->local.bExclusive){
			assert(nNumberOfBytesToUnlockLow == (DWORD)SHARED_SIZE);
			pFile->local.bExclusive = FALSE;
			pFile->shared->bExclusive = FALSE;
			bReturn = TRUE;
		}

		/* Did we just have a reader lock? */
		else if (pFile->local.nReaders)
		{
			assert(nNumberOfBytesToUnlockLow == (DWORD)SHARED_SIZE || nNumberOfBytesToUnlockLow == 1);
			pFile->local.nReaders --;
			if (pFile->local.nReaders == 0)
				pFile->shared->nReaders --;
			bReturn = TRUE;
		}
	}
	/* Releasing a pending lock */
	else if (dwFileOffsetLow == (DWORD)PENDING_BYTE && nNumberOfBytesToUnlockLow == 1)
	{
		if (pFile->local.bPending)
		{
			pFile->local.bPending = FALSE;
			pFile->shared->bPending = FALSE;
			bReturn = TRUE;
		}
	}
	/* Releasing a reserved lock */
	else if (dwFileOffsetLow == (DWORD)RESERVED_BYTE && nNumberOfBytesToUnlockLow == 1)
	{
		if (pFile->local.bReserved)
		{
			pFile->local.bReserved = FALSE;
			pFile->shared->bReserved = FALSE;
			bReturn = TRUE;
		}
	}
	winceMutexRelease(pFile->hMutex);
	return bReturn;
}

/*
** An implementation of the LockFileEx() API of windows for wince
*/
static BOOL winceLockFileEx(HANDLE *phFile, DWORD dwFlags, DWORD dwReserved, DWORD nNumberOfBytesToLockLow, DWORD nNumberOfBytesToLockHigh, LPOVERLAPPED lpOverlapped)
{
	UNUSED_PARAMETER(dwReserved);
	UNUSED_PARAMETER(nNumberOfBytesToLockHigh);
	/* If the caller wants a shared read lock, forward this call to winceLockFile */
	if (lpOverlapped->Offset == (DWORD)SHARED_FIRST && dwFlags == 1 && nNumberOfBytesToLockLow == (DWORD)SHARED_SIZE)
		return winceLockFile(phFile, SHARED_FIRST, 0, 1, 0);
	return FALSE;
}
#endif /* SYSTEM_OS_WINCE */

/*
** I/O METHODS@Begin ====================================================================================================
** The next group of routines implement the I/O methods specified by the system_io_methods object.
*/

/*
** Some microsoft compilers lack this definition.
*/
#ifndef INVALID_SET_FILE_POINTER
# define INVALID_SET_FILE_POINTER ((DWORD)-1)
#endif

/*
** Move the current position of the file handle passed as the first  argument to offset iOffset within the file. If successful, return 0. 
** Otherwise, set pFile->lastErrno and return non-zero.
*/
static int seekWinFile(winFile *pFile, INT64_TYPE iOffset)
{
	LONG upperBits;                 /* Most sig. 32 bits of new offset */
	LONG lowerBits;                 /* Least sig. 32 bits of new offset */
	DWORD dwRet;                    /* Value returned by SetFilePointer() */
	upperBits = (LONG)((iOffset>>32) & 0x7fffffff);
	lowerBits = (LONG)(iOffset & 0xffffffff);
	/*
	** API oddity: If successful, SetFilePointer() returns a dword containing the lower 32-bits of the new file-offset. Or, if it fails,
	** it returns INVALID_SET_FILE_POINTER. However according to MSDN, INVALID_SET_FILE_POINTER may also be a valid new offset. So to determine 
	** whether an error has actually occured, it is also necessary to call GetLastError().
	*/
	dwRet = SetFilePointer(pFile->h, lowerBits, &upperBits, FILE_BEGIN);
	if ((dwRet==INVALID_SET_FILE_POINTER && GetLastError()!=NO_ERROR))
	{
		pFile->lastErrno = GetLastError();
		return 1;
	}
	return 0;
}

/*
** Close a file.
**
** It is reported that an attempt to close a handle might sometimes fail.  This is a very unreasonable result, but windows is notorious
** for being unreasonable so I do not doubt that it might happen.  If the close fails, we pause for 100 milliseconds and try again.  As
** many as MX_CLOSE_ATTEMPT attempts to close the handle are made before giving up and returning an error.
*/
#define MX_CLOSE_ATTEMPT 3
static int winClose(system_file *id)
{
	int rc, cnt = 0;
	winFile *pFile = (winFile*)id;
	assert(id!=0);
	assert(pFile->pShm==0);
	OSTRACE(("CLOSE %d\n", pFile->h));
	do
	{
		rc = CloseHandle(pFile->h);
		/* SimulateIOError( rc=0; cnt=MX_CLOSE_ATTEMPT; ); */
	} while(rc==0 && ++cnt < MX_CLOSE_ATTEMPT && (Sleep(100), 1));
#if SYSTEM_OS_WINCE
	#define WINCE_DELETION_ATTEMPTS 3
	winceDestroyLock(pFile);
	if (pFile->zDeleteOnClose)
	{
		int cnt = 0;
		while(DeleteFileW(pFile->zDeleteOnClose)==0 && GetFileAttributesW(pFile->zDeleteOnClose)!=0xffffffff  && cnt++ < WINCE_DELETION_ATTEMPTS)
			Sleep(100);  /* Wait a little before trying again */
		free(pFile->zDeleteOnClose);
	}
#endif
	OSTRACE(("CLOSE %d %s\n", pFile->h, rc ? "ok" : "failed"));
	OpenCounter(-1);
	return (rc ? SYSTEM_OK : SYSTEM_IOERR);
}

/*
** Read data from a file into a buffer.  Return SYSTEM_OK if all bytes were read successfully and SYSTEM_IOERR if anything goes wrong.
*/
static int winRead(system_file *id, void *pBuf, int amt, INT64_TYPE offset)
/* (File to read from, Write content into this buffer, Number of bytes to read, Begin reading at this offset) */
{
	winFile *pFile = (winFile*)id;  /* file handle */
	DWORD nRead;                    /* Number of bytes actually read from file */
	assert(id!=0);
	SimulateIOError(return SYSTEM_IOERR_READ);
	OSTRACE(("READ %d lock=%d\n", pFile->h, pFile->locktype));
	if (seekWinFile(pFile, offset))
		return SYSTEM_FULL;
	if (!ReadFile(pFile->h, pBuf, amt, &nRead, 0))
	{
		pFile->lastErrno = GetLastError();
		return SYSTEM_IOERR_READ;
	}
	if(nRead<(DWORD)amt)
	{
		/* Unread parts of the buffer must be zero-filled */
		memset(&((char*)pBuf)[nRead], 0, amt-nRead);
		return SYSTEM_IOERR_SHORT_READ;
	}
	return SYSTEM_OK;
}

/*
** Write data from a buffer into a file.  Return SYSTEM_OK on success or some other error code on failure.
*/
static int winWrite(system_file *id, const void *pBuf, int amt, INT64_TYPE offset)
/* (File to write into, The bytes to be written, Number of bytes to write, Offset into the file to begin writing at) */
{
	int rc;                         /* True if error has occured, else false */
	winFile *pFile = (winFile*)id;  /* File handle */
	assert(amt>0);
	assert(pFile);
	SimulateIOError(return SYSTEM_IOERR_WRITE);
	SimulateDiskfullError(return SYSTEM_FULL);
	OSTRACE(("WRITE %d lock=%d\n", pFile->h, pFile->locktype));
	rc = seekWinFile(pFile, offset);
	if (rc==0)
	{
		u8 *aRem = (u8 *)pBuf;        /* Data yet to be written */
		int nRem = amt;               /* Number of bytes yet to be written */
		DWORD nWrite;                 /* Bytes written by each WriteFile() call */
		while (nRem>0 && WriteFile(pFile->h, aRem, nRem, &nWrite, 0) && nWrite>0)
		{
			aRem += nWrite;
			nRem -= nWrite;
		}
		if (nRem>0)
		{
			pFile->lastErrno = GetLastError();
			rc = 1;
		}
	}
	if (rc)
	{
		if (pFile->lastErrno==ERROR_HANDLE_DISK_FULL)
			return SYSTEM_FULL;
		return SYSTEM_IOERR_WRITE;
	}
	return SYSTEM_OK;
}

/*
** Truncate an open file to a specified size
*/
static int winTruncate(system_file *id, INT64_TYPE nByte)
{
	winFile *pFile = (winFile*)id;  /* File handle object */
	int rc = SYSTEM_OK;             /* Return code for this function */
	assert(pFile);
	OSTRACE(("TRUNCATE %d %lld\n", pFile->h, nByte));
	SimulateIOError(return SYSTEM_IOERR_TRUNCATE);
	/*
	** If the user has configured a chunk-size for this file, truncate the file so that it consists of an integer number of chunks (i.e. the
	** actual file size after the operation may be larger than the requested size).
	*/
	if (pFile->szChunk)
		nByte = ((nByte + pFile->szChunk - 1)/pFile->szChunk) * pFile->szChunk;
	/* SetEndOfFile() returns non-zero when successful, or zero when it fails. */
	if (seekWinFile(pFile, nByte))
		rc = SYSTEM_IOERR_TRUNCATE;
	else if (0==SetEndOfFile(pFile->h))
	{
		pFile->lastErrno = GetLastError();
		rc = SYSTEM_IOERR_TRUNCATE;
	}
	OSTRACE(("TRUNCATE %d %lld %s\n", pFile->h, nByte, rc ? "failed" : "ok"));
	return rc;
}

#ifdef SYSTEM_TEST
/*
** Count the number of fullsyncs and normal syncs.  This is used to test that syncs and fullsyncs are occuring at the right times.
*/
int system_sync_count = 0;
int system_fullsync_count = 0;
#endif

/*
** Make sure all writes to a particular file are committed to disk.
*/
static int winSync(system_file *id, int flags)
{
#if !defined(NDEBUG) || !defined(SYSTEM_NO_SYNC) || defined(SYSTEM_DEBUG)
	winFile *pFile = (winFile*)id;
#else
	UNUSED_PARAMETER(id);
#endif
	assert( pFile );
	/* Check that one of SYSTEM_SYNC_NORMAL or FULL was passed */
	assert((flags&0x0F)==SYSTEM_SYNC_NORMAL || (flags&0x0F)==SYSTEM_SYNC_FULL);
	OSTRACE(("SYNC %d lock=%d\n", pFile->h, pFile->locktype));
#ifndef SYSTEM_TEST
	UNUSED_PARAMETER(flags);
#else
	if (flags & SYSTEM_SYNC_FULL)
		system_fullsync_count++;
	system_sync_count++;
#endif
	/* Unix cannot, but some systems may return SYSTEM_FULL from here. This line is to test that doing so does not cause any problems. */
	SimulateDiskfullError(return SYSTEM_FULL);
	SimulateIOError(return SYSTEM_IOERR;);
	/* If we compiled with the SYSTEM_NO_SYNC flag, then syncing is a no-op */
#ifdef SYSTEM_NO_SYNC
	return SYSTEM_OK;
#else
	if (FlushFileBuffers(pFile->h))
		return SYSTEM_OK;
	else
	{
		pFile->lastErrno = GetLastError();
		return SYSTEM_IOERR;
	}
#endif
}

/*
** Determine the current size of a file in bytes
*/
static int winFileSize(system_file *id, INT64_TYPE *pSize)
{
	DWORD upperBits;
	DWORD lowerBits;
	winFile *pFile = (winFile*)id;
	DWORD error;
	assert(id!=0);
	SimulateIOError(return SYSTEM_IOERR_FSTAT);
	lowerBits = GetFileSize(pFile->h, &upperBits);
	if ((lowerBits == INVALID_FILE_SIZE) && ((error = GetLastError()) != NO_ERROR))
	{
		pFile->lastErrno = error;
		return SYSTEM_IOERR_FSTAT;
	}
	*pSize = (((INT64_TYPE)upperBits)<<32) + lowerBits;
	return SYSTEM_OK;
}

/*
** LOCKFILE_FAIL_IMMEDIATELY is undefined on some Windows systems.
*/
#ifndef LOCKFILE_FAIL_IMMEDIATELY
# define LOCKFILE_FAIL_IMMEDIATELY 1
#endif

/*
** Acquire a reader lock.
** Different API routines are called depending on whether or not this is Win95 or WinNT.
*/
static int getReadLock(winFile *pFile)
{
	int res;
	if (isNT())
	{
		OVERLAPPED ovlp;
		ovlp.Offset = SHARED_FIRST;
		ovlp.OffsetHigh = 0;
		ovlp.hEvent = 0;
		res = LockFileEx(pFile->h, LOCKFILE_FAIL_IMMEDIATELY, 0, SHARED_SIZE, 0, &ovlp);
		/* isNT() is 1 if SYSTEM_OS_WINCE==1, so this else is never executed. */
#if SYSTEM_OS_WINCE==0
	}
	else
	{
		int lk;
		system_randomness(sizeof(lk), &lk);
		pFile->sharedLockByte = (short)((lk & 0x7fffffff)%(SHARED_SIZE - 1));
		res = LockFile(pFile->h, SHARED_FIRST+pFile->sharedLockByte, 0, 1, 0);
#endif
	}
	if (res == 0)
		pFile->lastErrno = GetLastError();
	return res;
}

/*
** Undo a readlock
*/
static int unlockReadLock(winFile *pFile)
{
	int res;
	if (isNT())
	{
		res = UnlockFile(pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0);
		/* isNT() is 1 if SYSTEM_OS_WINCE==1, so this else is never executed. */
#if SYSTEM_OS_WINCE==0
	}
	else
	{
		res = UnlockFile(pFile->h, SHARED_FIRST + pFile->sharedLockByte, 0, 1, 0);
#endif
	}
	if (res == 0)
		pFile->lastErrno = GetLastError();
  return res;
}

/*
** Lock the file with the lock specified by parameter locktype - one of the following:
**
**     (1) SHARED_LOCK
**     (2) RESERVED_LOCK
**     (3) PENDING_LOCK
**     (4) EXCLUSIVE_LOCK
**
** Sometimes when requesting one lock state, additional lock states are inserted in between.  The locking might fail on one of the later
** transitions leaving the lock state different from what it started but still short of its goal.  The following chart shows the allowed
** transitions and the inserted intermediate states:
**
**    UNLOCKED -> SHARED
**    SHARED -> RESERVED
**    SHARED -> (PENDING) -> EXCLUSIVE
**    RESERVED -> (PENDING) -> EXCLUSIVE
**    PENDING -> EXCLUSIVE
**
** This routine will only increase a lock.  The winUnlock() routine erases all locks at once and returns us immediately to locking level 0.
** It is not possible to lower the locking level one step at a time.  You must go straight to locking level 0.
*/
static int winLock(system_file *id, int locktype)
{
	int rc = SYSTEM_OK;    /* Return code from subroutines */
	int res = 1;           /* Result of a windows lock call */
	int newLocktype;       /* Set pFile->locktype to this value before exiting */
	int gotPendingLock = 0;/* True if we acquired a PENDING lock this time */
	winFile *pFile = (winFile*)id;
	DWORD error = NO_ERROR;
	assert(id!=0);
	OSTRACE(("LOCK %d %d was %d(%d)\n", pFile->h, locktype, pFile->locktype, pFile->sharedLockByte));
	/* If there is already a lock of this type or more restrictive on the OsFile, do nothing. Don't use the end_lock: exit path, as sqlite3OsEnterMutex() hasn't been called yet. */
	if (pFile->locktype>=locktype)
		return SYSTEM_OK;
	/* Make sure the locking sequence is correct */
	assert(pFile->locktype!=NO_LOCK || locktype==SHARED_LOCK);
	assert(locktype!=PENDING_LOCK);
	assert(locktype!=RESERVED_LOCK || pFile->locktype==SHARED_LOCK);

	/* Lock the PENDING_LOCK byte if we need to acquire a PENDING lock or a SHARED lock.  If we are acquiring a SHARED lock, the acquisition of the PENDING_LOCK byte is temporary. */
	newLocktype = pFile->locktype;
	if ((pFile->locktype==NO_LOCK) || ((locktype==EXCLUSIVE_LOCK) && (pFile->locktype==RESERVED_LOCK)))
	{
		int cnt = 3;
		while( cnt-->0 && (res = LockFile(pFile->h, PENDING_BYTE, 0, 1, 0))==0 )
		{
			/* Try 3 times to get the pending lock.  The pending lock might be held by another reader process who will release it momentarily. */
			OSTRACE(("could not get a PENDING lock. cnt=%d\n", cnt));
			Sleep(1);
		}
		gotPendingLock = res;
		if (!res)
			error = GetLastError();
	}
	/* Acquire a shared lock */
	if (locktype==SHARED_LOCK && res)
	{
		assert(pFile->locktype==NO_LOCK);
		res = getReadLock(pFile);
		if (res)
			newLocktype = SHARED_LOCK;
		else
			error = GetLastError();
	}
	/* Acquire a RESERVED lock */
	if (locktype==RESERVED_LOCK && res)
	{
		assert(pFile->locktype==SHARED_LOCK);
		res = LockFile(pFile->h, RESERVED_BYTE, 0, 1, 0);
		if (res)
			newLocktype = RESERVED_LOCK;
		else
			error = GetLastError();
	}
	/* Acquire a PENDING lock */
	if (locktype==EXCLUSIVE_LOCK && res)
	{
		newLocktype = PENDING_LOCK;
		gotPendingLock = 0;
	}
	/* Acquire an EXCLUSIVE lock */
	if (locktype==EXCLUSIVE_LOCK && res)
	{
		assert( pFile->locktype>=SHARED_LOCK );
		res = unlockReadLock(pFile);
		OSTRACE(("unreadlock = %d\n", res));
		res = LockFile(pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0);
		if (res)
			newLocktype = EXCLUSIVE_LOCK;
		else
		{
			error = GetLastError();
			OSTRACE(("error-code = %d\n", error));
			getReadLock(pFile);
		}
	}
	/* If we are holding a PENDING lock that ought to be released, then release it now. */
	if (gotPendingLock && locktype==SHARED_LOCK)
		UnlockFile(pFile->h, PENDING_BYTE, 0, 1, 0);
	/* Update the state of the lock has held in the file descriptor then return the appropriate result code. */
	if (res)
	    rc = SYSTEM_OK;
	else
	{
		OSTRACE(("LOCK FAILED %d trying for %d but got %d\n", pFile->h, locktype, newLocktype));
		pFile->lastErrno = error;
		rc = SYSTEM_BUSY;
	}
	pFile->locktype = (u8)newLocktype;
	return rc;
}

/*
** This routine checks if there is a RESERVED lock held on the specified file by this or any other process. If such a lock is held, return non-zero, otherwise zero.
*/
static int winCheckReservedLock(system_file *id, int *pResOut)
{
	int rc;
	winFile *pFile = (winFile*)id;
	SimulateIOError(return SYSTEM_IOERR_CHECKRESERVEDLOCK;);
	assert(id!=0);
	if (pFile->locktype>=RESERVED_LOCK)
	{
		rc = 1;
		OSTRACE(("TEST WR-LOCK %d %d (local)\n", pFile->h, rc));
	}
	else
	{
		rc = LockFile(pFile->h, RESERVED_BYTE, 0, 1, 0);
		if (rc)
			UnlockFile(pFile->h, RESERVED_BYTE, 0, 1, 0);
		rc = !rc;
		OSTRACE(("TEST WR-LOCK %d %d (remote)\n", pFile->h, rc));
	}
	*pResOut = rc;
	return SYSTEM_OK;
}

/*
** Lower the locking level on file descriptor id to locktype.  locktype must be either NO_LOCK or SHARED_LOCK.
**
** If the locking level of the file descriptor is already at or below the requested locking level, this routine is a no-op.
**
** It is not possible for this routine to fail if the second argument is NO_LOCK.  If the second argument is SHARED_LOCK then this routine
** might return SYSTEM_IOERR;
*/
static int winUnlock(system_file *id, int locktype)
{
	int type;
	winFile *pFile = (winFile*)id;
	int rc = SYSTEM_OK;
	assert(pFile!=0);
	assert(locktype<=SHARED_LOCK);
	OSTRACE(("UNLOCK %d to %d was %d(%d)\n", pFile->h, locktype, pFile->locktype, pFile->sharedLockByte));
	type = pFile->locktype;
	if (type>=EXCLUSIVE_LOCK)
	{
		UnlockFile(pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0);
		if (locktype==SHARED_LOCK && !getReadLock(pFile))
			/* This should never happen.  We should always be able to reacquire the read lock */
			rc = SYSTEM_IOERR_UNLOCK;
	}
	if (type>=RESERVED_LOCK)
		UnlockFile(pFile->h, RESERVED_BYTE, 0, 1, 0);
	if (locktype==NO_LOCK && type>=SHARED_LOCK)
		unlockReadLock(pFile);
	if (type>=PENDING_LOCK)
		UnlockFile(pFile->h, PENDING_BYTE, 0, 1, 0);
	pFile->locktype = (u8)locktype;
	return rc;
}

/*
** Control and query of the open file handle.
*/
static int winFileControl(system_file *id, int op, void *pArg)
{
	switch (op)
	{
		case SYSTEM_FCNTL_LOCKSTATE:
		{
			*(int*)pArg = ((winFile*)id)->locktype;
			return SYSTEM_OK;
		}
		case SYSTEM_LAST_ERRNO:
		{
			*(int*)pArg = (int)((winFile*)id)->lastErrno;
			return SYSTEM_OK;
		}
		case SYSTEM_FCNTL_CHUNK_SIZE:
		{
			((winFile*)id)->szChunk = *(int*)pArg;
			return SYSTEM_OK;
		}
		case SYSTEM_FCNTL_SIZE_HINT:
		{
			INT64_TYPE sz = *(INT64_TYPE*)pArg;
			SimulateIOErrorBenign(1);
			winTruncate(id, sz);
			SimulateIOErrorBenign(0);
			return SYSTEM_OK;
		}
	}
	return SYSTEM_ERROR;
}

/*
** Return the sector size in bytes of the underlying block device for the specified file. This is almost always 512 bytes, but may be
** larger for some devices.
**
** APPID code assumes this function cannot fail. It also assumes that if two files are created in the same file-system directory (i.e.
** a database and its journal file) that the sector size will be the same for both.
*/
static int winSectorSize(system_file *id)
{
	assert(id!=0);
	return (int)(((winFile*)id)->sectorSize);
}

/*
** Return a vector of device characteristics.
*/
static int winDeviceCharacteristics(system_file *id)
{
	UNUSED_PARAMETER(id);
	return SYSTEM_IOCAP_UNDELETABLE_WHEN_OPEN;
}

#ifndef SYSTEM_OMIT_WAL

/* 
** Windows will only let you create file view mappings on allocation size granularity boundaries.
** During system_os_init() we do a GetSystemInfo() to get the granularity size.
*/
SYSTEM_INFO winSysInfo;

/*
** Helper functions to obtain and relinquish the global mutex. The global mutex is used to protect the winLockInfo objects used by 
** this file, all of which may be shared by multiple threads.
**
** Function winShmMutexHeld() is used to assert() that the global mutex  is held when required. This function is only used as part of assert() 
** statements. e.g.
**
**   winShmEnterMutex()
**   assert(winShmMutexHeld());
**   winShmLeaveMutex()
*/
static void winShmEnterMutex(void)
{
	system_mutex_enter(systemMutexAlloc(SYSTEM_MUTEX_STATIC_MASTER));
}
static void winShmLeaveMutex(void)
{
	system_mutex_leave(systemMutexAlloc(SYSTEM_MUTEX_STATIC_MASTER));
}
#ifdef SYSTEM_DEBUG
static int winShmMutexHeld(void)
{
	return system_mutex_held(systemMutexAlloc(SYSTEM_MUTEX_STATIC_MASTER));
}
#endif

/*
** Object used to represent a single file opened and mmapped to provide shared memory.  When multiple threads all reference the same
** log-summary, each thread has its own winFile object, but they all point to a single instance of this object.  In other words, each
** log-summary is opened only once per process.
**
** winShmMutexHeld() must be true when creating or destroying this object or while reading or writing the following fields:
**
**      nRef
**      pNext 
**
** The following fields are read-only after the object is created:
** 
**      fid
**      zFilename
**
** Either winShmNode.mutex must be held or winShmNode.nRef==0 and winShmMutexHeld() is true when reading or writing any other field in this structure.
*/
struct winShmNode
{
	system_mutex *mutex;       /* Mutex to access this object */
	char *zFilename;           /* Name of the file */
	winFile hFile;             /* File handle from winOpen */
	int szRegion;              /* Size of shared-memory regions */
	int nRegion;               /* Size of array apRegion */
	struct ShmRegion
	{
	    HANDLE hMap;           /* File handle from CreateFileMapping */
		void *pMap;
	} *aRegion;
	DWORD lastErrno;           /* The Windows errno from the last I/O error */
	int nRef;                  /* Number of winShm objects pointing to this */
	winShm *pFirst;            /* All winShm objects pointing to this */
	winShmNode *pNext;         /* Next in list of all winShmNode objects */
#ifdef SYSTEM_DEBUG
	u8 nextShmId;              /* Next available winShm.id value */
#endif
};

/*
** A global array of all winShmNode objects.
**
** The winShmMutexHeld() must be true while reading or writing this list.
*/
static winShmNode *winShmNodeList = 0;

/*
** Structure used internally by this VFS to record the state of an open shared memory connection.
**
** The following fields are initialized when this object is created and are read-only thereafter:
**
**    winShm.pShmNode
**    winShm.id
**
** All other fields are read/write.  The winShm.pShmNode->mutex must be held while accessing any read/write fields.
*/
struct winShm
{
	winShmNode *pShmNode;      /* The underlying winShmNode object */
	winShm *pNext;             /* Next winShm with the same winShmNode */
	u8 hasMutex;               /* True if holding the winShmNode mutex */
	u16 sharedMask;            /* Mask of shared locks held */
	u16 exclMask;              /* Mask of exclusive locks held */
#ifdef SYSTEM_DEBUG
	u8 id;                     /* Id of this connection with its winShmNode */
#endif
};

/*
** Constants used for locking
*/
#define WIN_SHM_BASE   ((22+SYSTEM_SHM_NLOCK)*4)        /* first lock byte */
#define WIN_SHM_DMS    (WIN_SHM_BASE+SYSTEM_SHM_NLOCK)  /* deadman switch */

/*
** Apply advisory locks for all n bytes beginning at ofst.
*/
#define _SHM_UNLCK  1
#define _SHM_RDLCK  2
#define _SHM_WRLCK  3
static int winShmSystemLock(winShmNode *pFile, int lockType, int ofst,int nByte)
/* (Apply locks to this open shared-memory segment, _SHM_UNLCK|_SHM_RDLCK|_SHM_WRLCK, Offset to first byte to be locked/unlocked, Number of bytes to lock or unlock) */
{
	OVERLAPPED ovlp;
	DWORD dwFlags;
	int rc = 0;           /* Result code form Lock/UnlockFileEx() */
	/* Access to the winShmNode object is serialized by the caller */
	assert(system_mutex_held(pFile->mutex) || pFile->nRef==0);
	/* Initialize the locking parameters */
	dwFlags = LOCKFILE_FAIL_IMMEDIATELY;
	if (lockType == _SHM_WRLCK)
		dwFlags |= LOCKFILE_EXCLUSIVE_LOCK;
	memset(&ovlp, 0, sizeof(OVERLAPPED));
	ovlp.Offset = ofst;
	/* Release/Acquire the system-level lock */
	if (lockType==_SHM_UNLCK)
		rc = UnlockFileEx(pFile->hFile.h, 0, nByte, 0, &ovlp);
	else
		rc = LockFileEx(pFile->hFile.h, dwFlags, 0, nByte, 0, &ovlp);
	if (rc!= 0)
		rc = SYSTEM_OK;
	else
	{
		pFile->lastErrno = GetLastError();
		rc = SYSTEM_BUSY;
	}
	OSTRACE(("SHM-LOCK %d %s %s 0x%08lx\n",  pFile->hFile.h, (rc==SYSTEM_OK ? "ok" : "failed"), (lockType==_SHM_UNLCK ? "UnlockFileEx" : "LockFileEx"), pFile->lastErrno));
	return rc;
}

/* Forward references to VFS methods */
static int winOpen(system_vfs*, const char*, system_file*, int, int*);
static int winDelete(system_vfs*, const char*, int);

/*
** Purge the winShmNodeList list of all entries with winShmNode.nRef==0.
**
** This is not a VFS shared-memory method; it is a utility function called by VFS shared-memory methods.
*/
static void winShmPurge(system_vfs *pVfs, int deleteFlag)
{
	winShmNode **pp;
	winShmNode *p;
	BOOL bRc;
	assert(winShmMutexHeld());
	pp = &winShmNodeList;
	while ((p = *pp)!=0)
		if (p->nRef==0)
		{
			int i;
			if(p->mutex)
				system_mutex_free(p->mutex);
			for (i=0; i<p->nRegion; i++)
			{
				bRc = UnmapViewOfFile(p->aRegion[i].pMap);
				OSTRACE(("SHM-PURGE pid-%d unmap region=%d %s\n", (int)GetCurrentProcessId(), i, (bRc ? "ok" : "failed")));
				bRc = CloseHandle(p->aRegion[i].hMap);
				OSTRACE(("SHM-PURGE pid-%d close region=%d %s\n", (int)GetCurrentProcessId(), i, (bRc ? "ok" : "failed")));
			}
			if (p->hFile.h != INVALID_HANDLE_VALUE)
			{
				SimulateIOErrorBenign(1);
				winClose((system_file*)&p->hFile);
				SimulateIOErrorBenign(0);
			}
			if (deleteFlag)
			{
				SimulateIOErrorBenign(1);
				winDelete(pVfs, p->zFilename, 0);
				SimulateIOErrorBenign(0);
			}
			*pp = p->pNext;
			system_free(p->aRegion);
			system_free(p);
		}
		else
			pp = &p->pNext;
}

/*
** Open the shared-memory area associated with database file pDbFd.
**
** When opening a new shared-memory file, if no other instances of that file are currently open, in this process or in other processes, then
** the file must be truncated to zero length or have its header cleared.
*/
static int winOpenSharedMemory(winFile *pDbFd)
{
	struct winShm *p;                  /* The connection to be opened */
	struct winShmNode *pShmNode = 0;   /* The underlying mmapped file */
	int rc;                            /* Result code */
	struct winShmNode *pNew;           /* Newly allocated winShmNode */
	int nName;                         /* Size of zName in bytes */
	assert(pDbFd->pShm==0);    /* Not previously opened */
	/* Allocate space for the new sqlite3_shm object.  Also speculatively allocate space for a new winShmNode and filename. */
	p = (struct winShm*)system_malloc(sizeof(*p));
	if (p==0)
		return SYSTEM_NOMEM;
	memset(p, 0, sizeof(*p));
	nName = systemStrlen30(pDbFd->zPath);
	pNew = (struct winShmNode*)system_malloc(sizeof(*pShmNode) + nName + 15);
	if (pNew==0)
	{
		system_free(p);
		return SYSTEM_NOMEM;
	}
	memset(pNew, 0, sizeof(*pNew));
	pNew->zFilename = (char*)&pNew[1];
	system_snprintf(nName+15, pNew->zFilename, "%s-shm", pDbFd->zPath);
	/* Look to see if there is an existing winShmNode that can be used. If no matching winShmNode currently exists, create a new one. */
	winShmEnterMutex();
	for (pShmNode = winShmNodeList; pShmNode; pShmNode=pShmNode->pNext)
		/* TBD need to come up with better match here.  Perhaps use FILE_ID_BOTH_DIR_INFO Structure. */
		if (systemStrICmp(pShmNode->zFilename, pNew->zFilename)==0)
			break;
	if (pShmNode)
		system_free(pNew);
	else
	{
		pShmNode = pNew;
		pNew = 0;
		((winFile*)(&pShmNode->hFile))->h = INVALID_HANDLE_VALUE;
		pShmNode->pNext = winShmNodeList;
		winShmNodeList = pShmNode;
		pShmNode->mutex = system_mutex_alloc(SYSTEM_MUTEX_FAST);
		if (pShmNode->mutex==0)
		{
			rc = SYSTEM_NOMEM;
			goto shm_open_err;
		}

		/* (vfs, ?, Name of the file (UTF-8), File handle here, Mode flags, ?) */
		rc = winOpen(pDbFd->pVfs, pShmNode->zFilename, (system_file*)&pShmNode->hFile,  SYSTEM_OPEN_WAL | SYSTEM_OPEN_READWRITE | SYSTEM_OPEN_CREATE, 0);
		if (SYSTEM_OK!=rc)
		{
			rc = SYSTEM_CANTOPEN_BKPT;
			goto shm_open_err;
		}

		/* Check to see if another process is holding the dead-man switch. If not, truncate the file to zero length.  */
		if (winShmSystemLock(pShmNode, _SHM_WRLCK, WIN_SHM_DMS, 1)==SYSTEM_OK)
		{
			rc = winTruncate((system_file *)&pShmNode->hFile, 0);
			if (rc!=SYSTEM_OK)
				rc = SYSTEM_IOERR_SHMOPEN;
		}
		if (rc==SYSTEM_OK)
		{
			winShmSystemLock(pShmNode, _SHM_UNLCK, WIN_SHM_DMS, 1);
			rc = winShmSystemLock(pShmNode, _SHM_RDLCK, WIN_SHM_DMS, 1);
		}
		if (rc)
			goto shm_open_err;
	}
	/* Make the new connection a child of the winShmNode */
	p->pShmNode = pShmNode;
#ifdef SYSTEM_DEBUG
	p->id = pShmNode->nextShmId++;
#endif
	pShmNode->nRef++;
	pDbFd->pShm = p;
	winShmLeaveMutex();
	/*
	** The reference count on pShmNode has already been incremented under the cover of the winShmEnterMutex() mutex and the pointer from the
	** new (struct winShm) object to the pShmNode has been set. All that is left to do is to link the new object into the linked list starting
	** at pShmNode->pFirst. This must be done while holding the pShmNode->mutex  mutex.
	*/
	system_mutex_enter(pShmNode->mutex);
	p->pNext = pShmNode->pFirst;
	pShmNode->pFirst = p;
	system_mutex_leave(pShmNode->mutex);
	return SYSTEM_OK;

	/* Jump here on any error */
shm_open_err:
	winShmSystemLock(pShmNode, _SHM_UNLCK, WIN_SHM_DMS, 1);
	winShmPurge(pDbFd->pVfs, 0);      /* This call frees pShmNode if required */
	system_free(p);
	system_free(pNew);
	winShmLeaveMutex();
	return rc;
}

/*
** Close a connection to shared-memory.  Delete the underlying  storage if deleteFlag is true.
*/
static int winShmUnmap(system_file *fd, int deleteFlag)
/* (Database holding shared memory, Delete after closing if true) */
{
	winFile *pDbFd;       /* Database holding shared-memory */
	winShm *p;            /* The connection to be closed */
	winShmNode *pShmNode; /* The underlying shared-memory file */
	winShm **pp;          /* For looping over sibling connections */
	pDbFd = (winFile*)fd;
	p = pDbFd->pShm;
	if (p==0)
		return SYSTEM_OK;
	pShmNode = p->pShmNode;
	/* Remove connection p from the set of connections associated with pShmNode */
	system_mutex_enter(pShmNode->mutex);
	for (pp=&pShmNode->pFirst; (*pp)!=p; pp = &(*pp)->pNext) { }
	*pp = p->pNext;
	/* Free the connection p */
	system_free(p);
	pDbFd->pShm = 0;
	system_mutex_leave(pShmNode->mutex);
	/* If pShmNode->nRef has reached 0, then close the underlying shared-memory file, too */
	winShmEnterMutex();
	assert(pShmNode->nRef>0);
	pShmNode->nRef--;
	if (pShmNode->nRef==0)
		winShmPurge(pDbFd->pVfs, deleteFlag);
	winShmLeaveMutex();
	return SYSTEM_OK;
}

/*
** Change the lock state for a shared-memory segment.
*/
static int winShmLock(system_file *fd, int ofst, int n, int flags)
/* (Database file holding the shared memory, First lock to acquire or release, Number of locks to acquire or release, What to do with the lock) */
{
	winFile *pDbFd = (winFile*)fd;        /* Connection holding shared memory */
	winShm *p = pDbFd->pShm;              /* The shared memory being locked */
	winShm *pX;                           /* For looping over all siblings */
	winShmNode *pShmNode = p->pShmNode;
	int rc = SYSTEM_OK;                   /* Result code */
	u16 mask;                             /* Mask of locks to take or release */
	assert(ofst>=0 && ofst+n<=SYSTEM_SHM_NLOCK);
	assert(n>=1);
	assert(flags==(SYSTEM_SHM_LOCK | SYSTEM_SHM_SHARED) || flags==(SYSTEM_SHM_LOCK | SYSTEM_SHM_EXCLUSIVE) || flags==(SYSTEM_SHM_UNLOCK | SYSTEM_SHM_SHARED) || flags==(SYSTEM_SHM_UNLOCK | SYSTEM_SHM_EXCLUSIVE));
	assert(n==1 || (flags & SYSTEM_SHM_EXCLUSIVE)!=0);
	mask = (u16)((1U<<(ofst+n)) - (1U<<ofst));
	assert(n>1 || mask==(1<<ofst));
	system_mutex_enter(pShmNode->mutex);
	if (flags & SYSTEM_SHM_UNLOCK)
	{
		u16 allMask = 0; /* Mask of locks held by siblings */
		/* See if any siblings hold this same lock */
		for (pX=pShmNode->pFirst; pX; pX=pX->pNext)
		{
			if (pX==p)
				continue;
			assert((pX->exclMask & (p->exclMask|p->sharedMask))==0);
			allMask |= pX->sharedMask;
		}
		/* Unlock the system-level locks */
		if ((mask & allMask)==0)
			rc = winShmSystemLock(pShmNode, _SHM_UNLCK, ofst+WIN_SHM_BASE, n);
		else
			rc = SYSTEM_OK;
		/* Undo the local locks */
		if (rc==SYSTEM_OK)
		{
			p->exclMask &= ~mask;
			p->sharedMask &= ~mask;
		} 
	}
	else if (flags & SYSTEM_SHM_SHARED)
	{
		u16 allShared = 0;  /* Union of locks held by connections other than "p" */

		/* Find out which shared locks are already held by sibling connections. If any sibling already holds an exclusive lock, go ahead and return SYSTEM_BUSY. */
		for (pX=pShmNode->pFirst; pX; pX=pX->pNext)
		{
			if ((pX->exclMask & mask)!=0)
			{
				rc = SYSTEM_BUSY;
				break;
			}
			allShared |= pX->sharedMask;
		}
		/* Get shared locks at the system level, if necessary */
		if (rc==SYSTEM_OK)
			if ((allShared & mask)==0)
				rc = winShmSystemLock(pShmNode, _SHM_RDLCK, ofst+WIN_SHM_BASE, n);
			else
				rc = SYSTEM_OK;
		/* Get the local shared locks */
		if (rc==SYSTEM_OK)
			p->sharedMask |= mask;
	}
	else
	{
		/* Make sure no sibling connections hold locks that will block this lock.  If any do, return SYSTEM_BUSY right away. */
		for (pX=pShmNode->pFirst; pX; pX=pX->pNext)
			if ((pX->exclMask & mask)!=0 || (pX->sharedMask & mask)!=0)
			{
				rc = SYSTEM_BUSY;
				break;
			}
		/* Get the exclusive locks at the system level.  Then if successful also mark the local connection as being locked. */
		if (rc==SYSTEM_OK)
		{
			rc = winShmSystemLock(pShmNode, _SHM_WRLCK, ofst+WIN_SHM_BASE, n);
			if (rc==SYSTEM_OK)
			{
				assert((p->sharedMask & mask)==0);
				p->exclMask |= mask;
			}
		}
	}
	system_mutex_leave(pShmNode->mutex);
	OSTRACE(("SHM-LOCK shmid-%d, pid-%d got %03x,%03x %s\n", p->id, (int)GetCurrentProcessId(), p->sharedMask, p->exclMask, (rc ? "failed" : "ok")));
	return rc;
}

/*
** Implement a memory barrier or memory fence on shared memory.  
**
** All loads and stores begun before the barrier must complete before any load or store begun after the barrier.
*/
static void winShmBarrier(system_file *fd)
 /* (Database holding the shared memory) */
{
	UNUSED_PARAMETER(fd);
	/* MemoryBarrier(); // does not work -- do not know why not */
	winShmEnterMutex();
	winShmLeaveMutex();
}

/*
** This function is called to obtain a pointer to region iRegion of the shared-memory associated with the database file fd. Shared-memory regions 
** are numbered starting from zero. Each shared-memory region is szRegion bytes in size.
**
** If an error occurs, an error code is returned and *pp is set to NULL.
**
** Otherwise, if the isWrite parameter is 0 and the requested shared-memory region has not been allocated (by any client, including one running in a
** separate process), then *pp is set to NULL and SYSTEM_OK returned. If isWrite is non-zero and the requested shared-memory region has not yet 
** been allocated, it is allocated by this function.
**
** If the shared-memory region has already been allocated or is allocated by this call as described above, then it is mapped into this processes 
** address space (if it is not already), *pp is set to point to the mapped  memory and SYSTEM_OK returned.
*/
static int winShmMap(system_file *fd, int iRegion, int szRegion, int isWrite, void volatile **pp)
/* (Handle open on database file, Region to retrieve, Size of regions, True to extend file if necessary, OUT: Mapped memory) */
{
	winFile *pDbFd = (winFile*)fd;
	winShm *p = pDbFd->pShm;
	winShmNode *pShmNode;
	int rc = SYSTEM_OK;
	if (!p)
	{
		rc = winOpenSharedMemory(pDbFd);
		if (rc!=SYSTEM_OK)
			return rc;
		p = pDbFd->pShm;
	}
	pShmNode = p->pShmNode;
	system_mutex_enter(pShmNode->mutex);
	assert(szRegion==pShmNode->szRegion || pShmNode->nRegion==0);
	if (pShmNode->nRegion<=iRegion)
	{
		struct ShmRegion *apNew;           /* New aRegion[] array */
		int nByte = (iRegion+1)*szRegion;  /* Minimum required file size */
		INT64_TYPE sz;                  /* Current size of wal-index file */
		pShmNode->szRegion = szRegion;
		/* The requested region is not mapped into this processes address space. Check to see if it has been allocated (i.e. if the wal-index file is large enough to contain the requested region). */
		rc = winFileSize((system_file*)&pShmNode->hFile, &sz);
		if (rc!=SYSTEM_OK)
		{
			rc = SYSTEM_IOERR_SHMSIZE;
			goto shmpage_out;
		}
		if (sz<nByte)
		{
			/*
			** The requested memory region does not exist. If isWrite is set to zero, exit early. *pp will be set to NULL and SYSTEM_OK returned.
			**
			** Alternatively, if isWrite is non-zero, use ftruncate() to allocate the requested memory region.
			*/
			if (!isWrite)
				goto shmpage_out;
			rc = winTruncate((system_file*)&pShmNode->hFile, nByte);
			if (rc!=SYSTEM_OK)
			{
				rc = SYSTEM_IOERR_SHMSIZE;
				goto shmpage_out;
			}
		}
		/* Map the requested memory region into this processes address space. */
		apNew = (struct ShmRegion*)system_realloc(pShmNode->aRegion, (iRegion+1)*sizeof(apNew[0]));
		if (!apNew)
		{
			rc = SYSTEM_IOERR_NOMEM;
			goto shmpage_out;
		}
		pShmNode->aRegion = apNew;
		//
		while (pShmNode->nRegion<=iRegion)
		{
			HANDLE hMap;                /* file-mapping handle */
			void *pMap = 0;             /* Mapped memory region */
			hMap = CreateFileMapping(pShmNode->hFile.h, NULL, PAGE_READWRITE, 0, nByte, NULL);
			OSTRACE(("SHM-MAP pid-%d create region=%d nbyte=%d %s\n", (int)GetCurrentProcessId(), pShmNode->nRegion, nByte, (hMap ? "ok" : "failed")));
			if (hMap)
			{
				int iOffset = pShmNode->nRegion*szRegion;
				int iOffsetShift = iOffset % winSysInfo.dwAllocationGranularity;
				pMap = MapViewOfFile(hMap, FILE_MAP_WRITE | FILE_MAP_READ, 0, iOffset - iOffsetShift, szRegion + iOffsetShift);
				OSTRACE(("SHM-MAP pid-%d map region=%d offset=%d size=%d %s\n", (int)GetCurrentProcessId(), pShmNode->nRegion, iOffset, szRegion, (pMap ? "ok" : "failed")));
			}
			if (!pMap)
			{
				pShmNode->lastErrno = GetLastError();
				rc = SYSTEM_IOERR;
				if (hMap)
					CloseHandle(hMap);
				goto shmpage_out;
			}
			pShmNode->aRegion[pShmNode->nRegion].pMap = pMap;
			pShmNode->aRegion[pShmNode->nRegion].hMap = hMap;
			pShmNode->nRegion++;
		}
	}
shmpage_out:
	if (pShmNode->nRegion>iRegion)
	{
		int iOffset = iRegion*szRegion;
		int iOffsetShift = iOffset % winSysInfo.dwAllocationGranularity;
		char *p = (char *)pShmNode->aRegion[iRegion].pMap;
		*pp = (void *)&p[iOffsetShift];
	}
	else
		*pp = 0;
	system_mutex_leave(pShmNode->mutex);
	return rc;
}

#else
# define winShmMap     0
# define winShmLock    0
# define winShmBarrier 0
# define winShmUnmap   0
#endif /* #ifndef SYSTEM_OMIT_WAL */

/*
** This vector defines all the methods that can operate on an system_file for win32.
*/
static const system_io_methods winIoMethod = {
	2,                              /* iVersion */
	winClose,                       /* xClose */
	winRead,                        /* xRead */
	winWrite,                       /* xWrite */
	winTruncate,                    /* xTruncate */
	winSync,                        /* xSync */
	winFileSize,                    /* xFileSize */
	winLock,                        /* xLock */
	winUnlock,                      /* xUnlock */
	winCheckReservedLock,           /* xCheckReservedLock */
	winFileControl,                 /* xFileControl */
	winSectorSize,                  /* xSectorSize */
	winDeviceCharacteristics,       /* xDeviceCharacteristics */
	winShmMap,                      /* xShmMap */
	winShmLock,                     /* xShmLock */
	winShmBarrier,                  /* xShmBarrier */
	winShmUnmap                     /* xShmUnmap */
};

/*
** Here ends the implementation of all system_file methods.
** ==================================================================================================== I/O METHODS@End
*/

/*
** VFS METHODS@Begin ==================================================================================================== 
** This division contains the implementation of methods on the sqlite3_vfs object.
*/

/*
** Convert a UTF-8 filename into whatever form the underlying operating system wants filenames in.  Space to hold the result
** is obtained from malloc and must be freed by the calling function.
*/
static void *convertUtf8Filename(const char *zFilename)
{
	void *zConverted = 0;
	if (isNT())
	{
		zConverted = utf8ToUnicode(zFilename);
	/* isNT() is 1 if SYSTEM_OS_WINCE==1, so this else is never executed.  */
#if SYSTEM_OS_WINCE==0
	}
	else
	{
		zConverted = utf8ToMbcs(zFilename);
#endif
	}
	/* caller will handle out of memory */
	return zConverted;
}

/*
** Create a temporary file name in zBuf.  zBuf must be big enough to hold at pVfs->mxPathname characters.
*/
static int getTempname(int nBuf, char *zBuf)
{
	static char zChars[] =
		"abcdefghijklmnopqrstuvwxyz"
		"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
		"0123456789";
	size_t i, j;
	char zTempPath[MAX_PATH+1];
	/* It's odd to simulate an io-error here, but really this is just using the io-error infrastructure to test that SQLite handles this function failing. */
	SimulateIOError( return SYSTEM_IOERR );
	if (system_temp_directory)
		system_snprintf(MAX_PATH-30, zTempPath, "%s", system_temp_directory);
	else if (isNT())
	{
		char *zMulti;
		WCHAR zWidePath[MAX_PATH];
		GetTempPathW(MAX_PATH-30, zWidePath);
		zMulti = unicodeToUtf8(zWidePath);
		if (zMulti)
		{
			system_snprintf(MAX_PATH-30, zTempPath, "%s", zMulti);
			free(zMulti);
		}
		else
			return SYSTEM_NOMEM;
/* isNT() is 1 if SYSTEM_OS_WINCE==1, so this else is never executed.  Since the ASCII version of these Windows API do not exist for WINCE, it's important to not reference them for WINCE builds. */
#if SYSTEM_OS_WINCE==0
	}
	else
	{
		char *zUtf8;
		char zMbcsPath[MAX_PATH];
		GetTempPathA(MAX_PATH-30, zMbcsPath);
		zUtf8 = system_win32_mbcs_to_utf8(zMbcsPath);
		if (zUtf8)
		{
			system_snprintf(MAX_PATH-30, zTempPath, "%s", zUtf8);
			free(zUtf8);
		}
		else
			return SYSTEM_NOMEM;
#endif
	}
	/* Check that the output buffer is large enough for the temporary file name. If it is not, return SYSTEM_ERROR. */
	if ((systemStrlen30(zTempPath) + systemStrlen30(SYSTEM_TEMP_FILE_PREFIX) + 17) >= nBuf )
		return SYSTEM_ERROR;
	for (i=systemStrlen30(zTempPath); i>0 && zTempPath[i-1]=='\\'; i--) { }
	zTempPath[i] = 0;
	system_snprintf(nBuf-17, zBuf, "%s\\"SYSTEM_TEMP_FILE_PREFIX, zTempPath);
	j = systemStrlen30(zBuf);
	system_randomness(15, &zBuf[j]);
	for (i=0; i<15; i++, j++)
		zBuf[j] = (char)zChars[((unsigned char)zBuf[j])%(sizeof(zChars)-1)];
	zBuf[j] = 0;
	OSTRACE(("TEMP FILENAME: %s\n", zBuf));
	return SYSTEM_OK; 
}

/*
** The return value of getLastErrorMsg is zero if the error message fits in the buffer, or non-zero otherwise (if the message was truncated).
*/
static int getLastErrorMsg(int nBuf, char *zBuf)
{
	/* FormatMessage returns 0 on failure.  Otherwise it returns the number of TCHARs written to the output buffer, excluding the terminating null char. */
	DWORD error = GetLastError();
	DWORD dwLen = 0;
	char *zOut = 0;
	if (isNT())
	{
		WCHAR *zTempWide = NULL;
		dwLen = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, error, 0, (LPWSTR)&zTempWide, 0, 0);
		if (dwLen > 0)
		{
			/* allocate a buffer and convert to UTF8 */
			zOut = unicodeToUtf8(zTempWide);
			/* free the system buffer allocated by FormatMessage */
			LocalFree(zTempWide);
		}
		/* isNT() is 1 if SYSTEM_OS_WINCE==1, so this else is never executed.  Since the ASCII version of these Windows API do not exist for WINCE, it's important to not reference them for WINCE builds. */
#if SYSTEM_OS_WINCE==0
	}
	else
	{
		char *zTemp = NULL;
		dwLen = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, error, 0, (LPSTR)&zTemp, 0, 0);
		if (dwLen > 0)
		{
			/* allocate a buffer and convert to UTF8 */
			zOut = system_win32_mbcs_to_utf8(zTemp);
			/* free the system buffer allocated by FormatMessage */
			LocalFree(zTemp);
		}
#endif
	}
	if (0 == dwLen)
		system_snprintf(nBuf, zBuf, "OsError 0x%x (%u)", error, error);
	else
	{
		/* copy a maximum of nBuf chars to output buffer */
		system_snprintf(nBuf, zBuf, "%s", zOut);
		/* free the UTF8 buffer */
		free(zOut);
	}
	return 0;
}

/*
** Open a file.
*/
static int winOpen(system_vfs *pVfs, const char *zName, system_file *id, int flags, int *pOutFlags)
/* (Not used, Name of the file (UTF-8), Write the SQLite file handle here, Open mode flags, Status return flags) */
{
	HANDLE h;
	DWORD dwDesiredAccess;
	DWORD dwShareMode;
	DWORD dwCreationDisposition;
	DWORD dwFlagsAndAttributes = 0;
#if SYSTEM_OS_WINCE
	int isTemp = 0;
#endif
	winFile *pFile = (winFile*)id;
	void *zConverted;              /* Filename in OS encoding */
	const char *zUtf8Name = zName; /* Filename in UTF-8 encoding */
	/* If argument zPath is a NULL pointer, this function is required to open a temporary file. Use this buffer to store the file name in. */
	char zTmpname[MAX_PATH+1];     /* Buffer used to create temp filename */
	int rc = SYSTEM_OK;            /* Function Return Code */
#if !defined(NDEBUG) || SYSTEM_OS_WINCE
	int eType = flags&0xFFFFFF00;  /* Type of file to open */
#endif
	int isExclusive  = (flags & SYSTEM_OPEN_EXCLUSIVE);
	int isDelete     = (flags & SYSTEM_OPEN_DELETEONCLOSE);
	int isCreate     = (flags & SYSTEM_OPEN_CREATE);
#ifndef NDEBUG
	int isReadonly   = (flags & SYSTEM_OPEN_READONLY);
#endif
	int isReadWrite  = (flags & SYSTEM_OPEN_READWRITE);
#ifndef NDEBUG
	int isOpenJournal = (isCreate && (eType==SYSTEM_OPEN_MASTER_JOURNAL || eType==SYSTEM_OPEN_MAIN_JOURNAL || eType==SYSTEM_OPEN_WAL));
#endif
	/* Check the following statements are true: 
	**
	**   (a) Exactly one of the READWRITE and READONLY flags must be set, and 
	**   (b) if CREATE is set, then READWRITE must also be set, and
	**   (c) if EXCLUSIVE is set, then CREATE must also be set.
	**   (d) if DELETEONCLOSE is set, then CREATE must also be set.
	*/
	assert((isReadonly==0 || isReadWrite==0) && (isReadWrite || isReadonly));
	assert(isCreate==0 || isReadWrite);
	assert(isExclusive==0 || isCreate);
	assert(isDelete==0 || isCreate);
	/* The main DB, main journal, WAL file and master journal are never automatically deleted. Nor are they ever temporary files.  */
	assert((!isDelete && zName) || eType!=SYSTEM_OPEN_MAIN_DB);
	assert((!isDelete && zName) || eType!=SYSTEM_OPEN_MAIN_JOURNAL);
	assert((!isDelete && zName) || eType!=SYSTEM_OPEN_MASTER_JOURNAL);
	assert((!isDelete && zName) || eType!=SYSTEM_OPEN_WAL);
	/* Assert that the upper layer has set one of the "file-type" flags. */
	assert(eType==SYSTEM_OPEN_MAIN_DB || eType==SYSTEM_OPEN_TEMP_DB || eType==SYSTEM_OPEN_MAIN_JOURNAL || eType==SYSTEM_OPEN_TEMP_JOURNAL || eType==SYSTEM_OPEN_SUBJOURNAL   || eType==SYSTEM_OPEN_MASTER_JOURNAL || eType==SYSTEM_OPEN_TRANSIENT_DB || eType==SYSTEM_OPEN_WAL);
	assert( id!=0 );
	UNUSED_PARAMETER(pVfs);
	pFile->h = INVALID_HANDLE_VALUE;
	/* If the second argument to this function is NULL, generate a  temporary file name to use  */
	if (!zUtf8Name)
	{
		assert(isDelete && !isOpenJournal);
		rc = getTempname(MAX_PATH+1, zTmpname);
		if (rc!=SYSTEM_OK)
			return rc;
		zUtf8Name = zTmpname;
	}
	/* Convert the filename to the system encoding. */
	zConverted = convertUtf8Filename(zUtf8Name);
	if (zConverted==0)
		return SYSTEM_NOMEM;
	if (isReadWrite)
		dwDesiredAccess = GENERIC_READ | GENERIC_WRITE;
	else
		dwDesiredAccess = GENERIC_READ;
	/* SYSTEM_OPEN_EXCLUSIVE is used to make sure that a new file is created. SQLite doesn't use it to indicate "exclusive access" as it is usually understood. */
	if (isExclusive)
		/* Creates a new file, only if it does not already exist. If the file exists, it fails. */
		dwCreationDisposition = CREATE_NEW;
	else if (isCreate)
		/* Open existing file, or create if it doesn't exist */
		dwCreationDisposition = OPEN_ALWAYS;
	else
		/* Opens a file, only if it exists. */
		dwCreationDisposition = OPEN_EXISTING;
	dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
	if (isDelete)
	{
#if SYSTEM_OS_WINCE
		dwFlagsAndAttributes = FILE_ATTRIBUTE_HIDDEN;
		isTemp = 1;
#else
		dwFlagsAndAttributes = FILE_ATTRIBUTE_TEMPORARY | FILE_ATTRIBUTE_HIDDEN | FILE_FLAG_DELETE_ON_CLOSE;
#endif
	}
	else
		dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL;
  /* Reports from the internet are that performance is always better if FILE_FLAG_RANDOM_ACCESS is used. */
#if SYSTEM_OS_WINCE
	dwFlagsAndAttributes |= FILE_FLAG_RANDOM_ACCESS;
#endif
	if (isNT())
	{
		h = CreateFileW((WCHAR*)zConverted, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, dwFlagsAndAttributes, NULL);
		/* isNT() is 1 if SYSTEM_OS_WINCE==1, so this else is never executed. Since the ASCII version of these Windows API do not exist for WINCE, it's important to not reference them for WINCE builds. */
#if SYSTEM_OS_WINCE==0
	}
	else
	{
		h = CreateFileA((char*)zConverted, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, dwFlagsAndAttributes, NULL);
#endif
	}
	OSTRACE(("OPEN %d %s 0x%lx %s\n", h, zName, dwDesiredAccess, (h==INVALID_HANDLE_VALUE ? "failed" : "ok")));
	if (h==INVALID_HANDLE_VALUE)
	{
		pFile->lastErrno = GetLastError();
		free(zConverted);
		if (isReadWrite)
			return winOpen(pVfs, zName, id, ((flags|SYSTEM_OPEN_READONLY)&~(SYSTEM_OPEN_CREATE|SYSTEM_OPEN_READWRITE)), pOutFlags);
		else
			return SYSTEM_CANTOPEN_BKPT;
	}
	if (pOutFlags)
		if (isReadWrite)
			*pOutFlags = SYSTEM_OPEN_READWRITE;
		else
			*pOutFlags = SYSTEM_OPEN_READONLY;
	memset(pFile, 0, sizeof(*pFile));
	pFile->pMethod = &winIoMethod;
	pFile->h = h;
	pFile->lastErrno = NO_ERROR;
	pFile->pVfs = pVfs;
	pFile->pShm = 0;
	pFile->zPath = zName;
	pFile->sectorSize = getSectorSize(pVfs, zUtf8Name);
#if SYSTEM_OS_WINCE
	if (isReadWrite && eType==SYSTEM_OPEN_MAIN_DB && !winceCreateLock(zName, pFile))
	{
		CloseHandle(h);
		free(zConverted);
		return SYSTEM_CANTOPEN_BKPT;
	}
	if (isTemp)
		pFile->zDeleteOnClose = zConverted;
	else
#endif
	{
		free(zConverted);
	}
	OpenCounter(+1);
	return rc;
}

/*
** Delete the named file.
**
** Note that windows does not allow a file to be deleted if some other process has it open.  Sometimes a virus scanner or indexing program
** will open a journal file shortly after it is created in order to do whatever it does.  While this other process is holding the
** file open, we will be unable to delete it.  To work around this problem, we delay 100 milliseconds and try to delete again.  Up
** to MX_DELETION_ATTEMPTs deletion attempts are run before giving up and returning an error.
*/
#define MX_DELETION_ATTEMPTS 5
static int winDelete(system_vfs *pVfs, const char *zFilename, int syncDir)
/* (Not used on win32, Name of file to delete, Not used on win32) */
{
	int cnt = 0;
	DWORD rc;
	DWORD error = 0;
	void *zConverted;
	UNUSED_PARAMETER(pVfs);
	UNUSED_PARAMETER(syncDir);
	SimulateIOError(return SYSTEM_IOERR_DELETE);
	zConverted = convertUtf8Filename(zFilename);
	if (zConverted==0)
		return SYSTEM_NOMEM;
	if (isNT())
	{
		do
		{
			DeleteFileW((LPCWSTR)zConverted);
		} while((((rc = GetFileAttributesW((LPCWSTR)zConverted)) != INVALID_FILE_ATTRIBUTES) || ((error = GetLastError()) == ERROR_ACCESS_DENIED)) && (++cnt < MX_DELETION_ATTEMPTS) && (Sleep(100), 1));
		/* isNT() is 1 if SYSTEM_OS_WINCE==1, so this else is never executed. Since the ASCII version of these Windows API do not exist for WINCE, it's important to not reference them for WINCE builds. */
#if SYSTEM_OS_WINCE==0
	}
	else
	{
		do
		{
			DeleteFileA((LPCSTR)zConverted);
		} while((((rc = GetFileAttributesA((LPCSTR)zConverted)) != INVALID_FILE_ATTRIBUTES) || ((error = GetLastError()) == ERROR_ACCESS_DENIED)) && (++cnt < MX_DELETION_ATTEMPTS) && (Sleep(100), 1));
#endif
	}
	free(zConverted);
	OSTRACE(("DELETE \"%s\" %s\n", zFilename, ((rc==INVALID_FILE_ATTRIBUTES) && (error==ERROR_FILE_NOT_FOUND) ? "ok" : "failed")));
	return ((rc == INVALID_FILE_ATTRIBUTES) && (error == ERROR_FILE_NOT_FOUND) ? SYSTEM_OK : SYSTEM_IOERR_DELETE);
}

/*
** Check the existance and status of a file.
*/
static int winAccess(system_vfs *pVfs, const char *zFilename, int flags, int *pResOut)
/* (Not used on win32, Name of file to check, Type of test to make on this file, OUT: Result) */
{
	DWORD attr;
	int rc = 0;
	void *zConverted;
	UNUSED_PARAMETER(pVfs);
	SimulateIOError( return SYSTEM_IOERR_ACCESS; );
	zConverted = convertUtf8Filename(zFilename);
	if (zConverted==0)
		return SYSTEM_NOMEM;
	if (isNT())
	{
		WIN32_FILE_ATTRIBUTE_DATA sAttrData;
		memset(&sAttrData, 0, sizeof(sAttrData));
		if (GetFileAttributesExW((WCHAR*)zConverted, GetFileExInfoStandard, &sAttrData))
			/* For an SYSTEM_ACCESS_EXISTS query, treat a zero-length file as if it does not exist. */
			if (flags==SYSTEM_ACCESS_EXISTS && sAttrData.nFileSizeHigh==0  && sAttrData.nFileSizeLow==0)
				attr = INVALID_FILE_ATTRIBUTES;
			else
				attr = sAttrData.dwFileAttributes;
		else
			if (GetLastError()!=ERROR_FILE_NOT_FOUND)
			{
				free(zConverted);
				return SYSTEM_IOERR_ACCESS;
			}
			else
				attr = INVALID_FILE_ATTRIBUTES;
	/* isNT() is 1 if SYSTEM_OS_WINCE==1, so this else is never executed. Since the ASCII version of these Windows API do not exist for WINCE, it's important to not reference them for WINCE builds. */
#if SYSTEM_OS_WINCE==0
	}
	else
	{
		attr = GetFileAttributesA((char*)zConverted);
#endif
	}
	free(zConverted);
	switch (flags)
	{
		case SYSTEM_ACCESS_READ:
		case SYSTEM_ACCESS_EXISTS:
			rc = attr!=INVALID_FILE_ATTRIBUTES;
			break;
		case SYSTEM_ACCESS_READWRITE:
			rc = (attr & FILE_ATTRIBUTE_READONLY)==0;
			break;
		default:
			assert(!"Invalid flags argument");
	}
	*pResOut = rc;
	return SYSTEM_OK;
}


/*
** Turn a relative pathname into a full pathname.  Write the full pathname into zOut[].  zOut[] will be at least pVfs->mxPathname
** bytes in size.
*/
static int winFullPathname(system_vfs *pVfs, const char *zRelative, int nFull, char *zFull)
/* (Pointer to vfs object, Possibly relative input path, Size of output buffer in bytes, Output buffer */
{  
#if defined(__CYGWIN__)
	SimulateIOError( return SYSTEM_ERROR );
	UNUSED_PARAMETER(nFull);
	cygwin_conv_to_full_win32_path(zRelative, zFull);
	return SYSTEM_OK;
#endif
#if SYSTEM_OS_WINCE
	SimulateIOError( return SYSTEM_ERROR );
	UNUSED_PARAMETER(nFull);
	/* WinCE has no concept of a relative pathname, or so I am told. */
	system_snprintf(pVfs->mxPathname, zFull, "%s", zRelative);
	return SYSTEM_OK;
#endif
#if !SYSTEM_OS_WINCE && !defined(__CYGWIN__)
	int nByte;
	void *zConverted;
	char *zOut;
	/*
	** It's odd to simulate an io-error here, but really this is just using the io-error infrastructure to test that SQLite handles this
	** function failing. This function could fail if, for example, the current working directory has been unlinked.
	*/
	SimulateIOError( return SYSTEM_ERROR );
	UNUSED_PARAMETER(nFull);
	zConverted = convertUtf8Filename(zRelative);
	if (isNT())
	{
		WCHAR *zTemp;
		nByte = GetFullPathNameW((WCHAR*)zConverted, 0, 0, 0) + 3;
		zTemp = (WCHAR*)malloc(nByte*sizeof(zTemp[0]));
		if (zTemp==0)
		{
			free(zConverted);
			return SYSTEM_NOMEM;
		}
		GetFullPathNameW((WCHAR*)zConverted, nByte, zTemp, 0);
		free(zConverted);
		zOut = unicodeToUtf8(zTemp);
		free(zTemp);
		/* isNT() is 1 if SYSTEM_OS_WINCE==1, so this else is never executed. Since the ASCII version of these Windows API do not exist for WINCE, it's important to not reference them for WINCE builds. */
#if SYSTEM_OS_WINCE==0
	}
	else
	{
		char *zTemp;
		nByte = GetFullPathNameA((char*)zConverted, 0, 0, 0) + 3;
		zTemp = (char*)malloc(nByte*sizeof(zTemp[0]));
		if (zTemp==0)
		{
			free(zConverted);
			return SYSTEM_NOMEM;
		}
		GetFullPathNameA((char*)zConverted, nByte, zTemp, 0);
		free(zConverted);
		zOut = system_win32_mbcs_to_utf8(zTemp);
		free(zTemp);
#endif
	}
	if (zOut)
	{
		system_snprintf(pVfs->mxPathname, zFull, "%s", zOut);
		free(zOut);
		return SYSTEM_OK;
	}
	else
		return SYSTEM_NOMEM;
#endif
}

/*
** Get the sector size of the device used to store file.
*/
static int getSectorSize(system_vfs *pVfs, const char *zRelative)
/* UTF-8 file name */
{
	DWORD bytesPerSector = SYSTEM_DEFAULT_SECTOR_SIZE;
	/* GetDiskFreeSpace is not supported under WINCE */
#if SYSTEM_OS_WINCE
	UNUSED_PARAMETER(pVfs);
	UNUSED_PARAMETER(zRelative);
#else
	char zFullpath[MAX_PATH+1];
	int rc;
	DWORD dwRet = 0;
	DWORD dwDummy;
	/* We need to get the full path name of the file to get the drive letter to look up the sector size. */
	SimulateIOErrorBenign(1);
	rc = winFullPathname(pVfs, zRelative, MAX_PATH, zFullpath);
	SimulateIOErrorBenign(0);
	if (rc == SYSTEM_OK)
	{
		void *zConverted = convertUtf8Filename(zFullpath);
		if (zConverted)
			if (isNT())
			{
				/* trim path to just drive reference */
				WCHAR *p = (WCHAR*)zConverted;
				for (;*p;p++)
					if( *p == '\\' )
					{
						*p = '\0';
						break;
					}
				dwRet = GetDiskFreeSpaceW((WCHAR*)zConverted, &dwDummy, &bytesPerSector, &dwDummy, &dwDummy);
			}
			else
			{
				/* trim path to just drive reference */
				char *p = (char*)zConverted;
				for (;*p;p++)
					if (*p == '\\')
					{
						*p = '\0';
						break;
					}
				dwRet = GetDiskFreeSpaceA((char*)zConverted, &dwDummy, &bytesPerSector, &dwDummy, &dwDummy);
			}
			free(zConverted);
		if (!dwRet)
			bytesPerSector = SYSTEM_DEFAULT_SECTOR_SIZE;
	}
#endif
	return (int)bytesPerSector; 
}

#ifndef SYSTEM_OMIT_LOAD_EXTENSION
/*
** Interfaces for opening a shared library, finding entry points within the shared library, and closing the shared library.
*/
/*
** Interfaces for opening a shared library, finding entry points within the shared library, and closing the shared library.
*/
static void *winDlOpen(system_vfs *pVfs, const char *zFilename)
{
	HANDLE h;
	void *zConverted = convertUtf8Filename(zFilename);
	UNUSED_PARAMETER(pVfs);
	if (zConverted==0)
		return 0;
	if (isNT())
	{
		h = LoadLibraryW((WCHAR*)zConverted);
		/* isNT() is 1 if SYSTEM_OS_WINCE==1, so this else is never executed. Since the ASCII version of these Windows API do not exist for WINCE, it's important to not reference them for WINCE builds. */
#if SYSTEM_OS_WINCE==0
	}
	else
	{
		h = LoadLibraryA((char*)zConverted);
#endif
	}
	free(zConverted);
	return (void*)h;
}

static void winDlError(system_vfs *pVfs, int nBuf, char *zBufOut)
{
	UNUSED_PARAMETER(pVfs);
	getLastErrorMsg(nBuf, zBufOut);
}

void (*winDlSym(system_vfs *pVfs, void *pHandle, const char *zSymbol))(void)
{
	UNUSED_PARAMETER(pVfs);
#if SYSTEM_OS_WINCE
	/* The GetProcAddressA() routine is only available on wince. */
	return (void(*)(void))GetProcAddressA((HANDLE)pHandle, zSymbol);
#else
	/* All other windows platforms expect GetProcAddress() to take an Ansi string regardless of the _UNICODE setting */
	return (void(*)(void))GetProcAddress((HMODULE)pHandle, zSymbol);
#endif
}

void winDlClose(system_vfs *pVfs, void *pHandle)
{
	UNUSED_PARAMETER(pVfs);
	FreeLibrary((HMODULE)pHandle);
}
#else /* if SYSTEM_OMIT_LOAD_EXTENSION is defined: */
	#define winDlOpen  0
	#define winDlError 0
	#define winDlSym   0
	#define winDlClose 0
#endif

/*
** Write up to nBuf bytes of randomness into zBuf.
*/
static int winRandomness(system_vfs *pVfs, int nBuf, char *zBuf)
{
	int n = 0;
	UNUSED_PARAMETER(pVfs);
#if defined(SYSTEM_TEST)
	n = nBuf;
	memset(zBuf, 0, nBuf);
#else
	if (sizeof(SYSTEMTIME)<=nBuf-n)
	{
		SYSTEMTIME x;
		GetSystemTime(&x);
		memcpy(&zBuf[n], &x, sizeof(x));
		n += sizeof(x);
	}
	if (sizeof(DWORD)<=nBuf-n)
	{
		DWORD pid = GetCurrentProcessId();
		memcpy(&zBuf[n], &pid, sizeof(pid));
		n += sizeof(pid);
	}
	if (sizeof(DWORD)<=nBuf-n)
	{
		DWORD cnt = GetTickCount();
		memcpy(&zBuf[n], &cnt, sizeof(cnt));
		n += sizeof(cnt);
	}
	if (sizeof(LARGE_INTEGER)<=nBuf-n)
	{
		LARGE_INTEGER i;
		QueryPerformanceCounter(&i);
		memcpy(&zBuf[n], &i, sizeof(i));
		n += sizeof(i);
	}
#endif
	return n;
}

/*
** Sleep for a little while.  Return the amount of time slept.
*/
static int winSleep(system_vfs *pVfs, int microsec)
{
	Sleep((microsec+999)/1000);
	UNUSED_PARAMETER(pVfs);
	return ((microsec+999)/1000)*1000;
}

/*
** The following variable, if set to a non-zero value, is interpreted as the number of seconds since 1970 and is used to set the result of
** systemOsCurrentTime() during testing.
*/
#ifdef SYSTEM_TEST
int system_current_time = 0;  /* Fake system time in seconds since 1970. */
#endif

/*
** Find the current time (in Universal Coordinated Time).  Write into *piNow the current time and date as a Julian Day number times 86_400_000.  In
** other words, write into *piNow the number of milliseconds since the Julian epoch of noon in Greenwich on November 24, 4714 B.C according to the
** proleptic Gregorian calendar.
**
** On success, return 0.  Return 1 if the time and date cannot be found.
*/
static int winCurrentTimeInt64(system_vfs *pVfs, INT64_TYPE *piNow)
{
	/* FILETIME structure is a 64-bit value representing the number of 100-nanosecond intervals since January 1, 1601 (= JD 2305813.5). */
	FILETIME ft;
	static const INT64_TYPE winFiletimeEpoch = 23058135*(INT64_TYPE)8640000;
#ifdef SYSTEM_TEST
	static const INT64_TYPE unixEpoch = 24405875*(INT64_TYPE)8640000;
#endif
	/* 2^32 - to avoid use of LL and warnings in gcc */
	static const INT64_TYPE max32BitValue = (INT64_TYPE)2000000000 + (INT64_TYPE)2000000000 + (INT64_TYPE)294967296;
#if SYSTEM_OS_WINCE
	SYSTEMTIME time;
	GetSystemTime(&time);
	/* if SystemTimeToFileTime() fails, it returns zero. */
	if (!SystemTimeToFileTime(&time,&ft))
		return 1;
#else
	GetSystemTimeAsFileTime(&ft);
#endif
	*piNow = winFiletimeEpoch + ((((INT64_TYPE)ft.dwHighDateTime)*max32BitValue) + (INT64_TYPE)ft.dwLowDateTime)/(INT64_TYPE)10000;
#ifdef SYSTEM_TEST
	if (system_current_time)
		*piNow = 1000*(INT64_TYPE)system_current_time + unixEpoch;
#endif
	UNUSED_PARAMETER(pVfs);
	return 0;
}

/*
** Find the current time (in Universal Coordinated Time).  Write the current time and date as a Julian Day number into *prNow and
** return 0.  Return 1 if the time and date cannot be found.
*/
int winCurrentTime(system_vfs *pVfs, double *prNow)
{
	int rc;
	INT64_TYPE i;
	rc = winCurrentTimeInt64(pVfs, &i);
	if (!rc)
		*prNow = i/86400000.0;
	return rc;
}

/*
** The idea is that this function works like a combination of GetLastError() and FormatMessage() on windows (or errno and
** strerror_r() on unix). After an error is returned by an OS function, APPID calls this function with zBuf pointing to
** a buffer of nBuf bytes. The OS layer should populate the buffer with a nul-terminated UTF-8 encoded error message
** describing the last IO error to have occurred within the calling thread.
**
** If the error message is too large for the supplied buffer, it should be truncated. The return value of xGetLastError
** is zero if the error message fits in the buffer, or non-zero otherwise (if the message was truncated). If non-zero is returned,
** then it is not necessary to include the nul-terminator character in the output buffer.
**
** Not supplying an error message will have no adverse effect on APPID. It is fine to have an implementation that never
** returns an error message:
**
**   int xGetLastError(system_vfs *pVfs, int nBuf, char *zBuf)
**   {
**      assert(zBuf[0]=='\0');
**      return 0;
**   }
**
** However if an error message is supplied, it will be incorporated by APPID into the error message available to the user using
** system_errmsg(), possibly making IO errors easier to debug.
*/
static int winGetLastError(system_vfs *pVfs, int nBuf, char *zBuf)
{
	UNUSED_PARAMETER(pVfs);
	return getLastErrorMsg(nBuf, zBuf);
}

/*
** Initialize and deinitialize the operating system interface.
*/
int system_os_init(void)
{
	static system_vfs winVfs = {
		2,                   /* iVersion */
		sizeof(winFile),     /* szOsFile */
		MAX_PATH,            /* mxPathname */
		0,                   /* pNext */
		"win32",             /* zName */
		0,                   /* pAppData */
		winOpen,             /* xOpen */
		winDelete,           /* xDelete */
		winAccess,           /* xAccess */
		winFullPathname,     /* xFullPathname */
		winDlOpen,           /* xDlOpen */
		winDlError,          /* xDlError */
		winDlSym,            /* xDlSym */
		winDlClose,          /* xDlClose */
		winRandomness,       /* xRandomness */
		winSleep,            /* xSleep */
		winCurrentTime,      /* xCurrentTime */
		winGetLastError,     /* xGetLastError */
		winCurrentTimeInt64, /* xCurrentTimeInt64 */
	};
#ifndef SYSTEM_OMIT_WAL
	/* get memory map allocation granularity */
	memset(&winSysInfo, 0, sizeof(SYSTEM_INFO));
	GetSystemInfo(&winSysInfo);
	assert(winSysInfo.dwAllocationGranularity > 0);
#endif
	system_vfs_register(&winVfs, 1);
	return SYSTEM_OK; 
}

int system_os_end(void)
{
  return SYSTEM_OK;
}

#endif /* SYSTEM_OS_WIN */
